#include "DFRobot_K10BleUart.h"

static BLEServer *pServer = NULL;
static bool deviceConnected = false;
static bool oldDeviceConnected = false;
static BLECharacteristic * pServerCharacteristic;
static BLEAdvertisedDevice* myDevice;
static BLEScan* pBLEScan;
BLEClient* pClient;
static BLERemoteCharacteristic* pMasterCharacteristic;
static bool deviceConnectedMaster = false;
static boolean doConnect = false;
static boolean connected = false;
static boolean doScan = false;
static uint8_t sendMode = 0;

bleCallback _bleCallback = NULL;
static String _masterBleName = "null";

#define SERVICE_UUID           "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" 
#define CHARACTERISTIC_UUID "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"

void taskBleServer(void *arg)
{
  while(1){
     // disconnecting
    if (!deviceConnected && oldDeviceConnected) {
      delay(500); // give the bluetooth stack the chance to get things ready
      pServer->startAdvertising(); // restart advertising
      oldDeviceConnected = deviceConnected;
    }
    // connecting
    if (deviceConnected && !oldDeviceConnected) {
	  // do stuff here on connecting
      oldDeviceConnected = deviceConnected;
    }
    vTaskDelay(1);
  }
 
}

class MyClientCallback : public BLEClientCallbacks {
  void onConnect(BLEClient* pclient) {
  }

  void onDisconnect(BLEClient* pclient) {
    doScan = true;
    Serial.println("onDisconnect");
  }
};


static void notifyCallback(
  BLERemoteCharacteristic* pBLERemoteCharacteristic,
  uint8_t* pData,
  size_t length,
  bool isNotify) {
    char* _data = (char*)malloc(length+1);
    _data[length] = '\0';
    memcpy(_data,pData, length);
    String sendData =String(_data);
    if (_bleCallback != NULL){
      _bleCallback(sendData);
    }
    free(_data);
}

// 连接到目标设备
bool connectToServer() {
  pClient  = BLEDevice::createClient();
  pClient->setClientCallbacks(new MyClientCallback());

  Serial.println("正在连接到设备...");
  pClient->connect(myDevice);
  pClient->setMTU(517);
  BLERemoteService* pService = pClient->getService(SERVICE_UUID);  // 获取目标服务
    if (pService != nullptr) {
      pMasterCharacteristic = pService->getCharacteristic(CHARACTERISTIC_UUID);  // 获取特征
      if (pMasterCharacteristic != nullptr) {
        deviceConnectedMaster = true;
        Serial.println("准备好进行数据通信！");
        pMasterCharacteristic->registerForNotify(notifyCallback);  // 注册通知回调函数
      } else {
        pClient->disconnect();
        Serial.println("无法找到特征！");
        return false;
      }
    } else {
      pClient->disconnect();
      Serial.println("无法找到服务！");
      return false;
    }
  return true;
}

void taskBleMaster(void *arg)
{
  while(1){
    if (doConnect == true) {
      if (connectToServer()) {
        Serial.println("We are now connected to the BLE Server.");
      } else {
        Serial.println("We have failed to connect to the server; there is nothin more we will do.");
      }
      doConnect = false;
    }
    if(doScan){
      BLEDevice::getScan()->start(0);  // this is just example to start scan after disconnect, most likely there is better way to do it in arduino
    }
    delay(1000); // Delay a second between loops.
  }

}





// 蓝牙设备扫描回调函数
class AdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
  void onResult(BLEAdvertisedDevice advertisedDevice) {
    Serial.print("扫描到设备：");
    Serial.println(advertisedDevice.toString().c_str());

    if (String(advertisedDevice.getName().c_str()) == _masterBleName) {
      BLEDevice::getScan()->stop(); 
      Serial.println("找到目标设备，准备连接...");
      myDevice = new BLEAdvertisedDevice(advertisedDevice); 
      doConnect = true;
      doScan = false;
    }
  }
};

class MyCallbacks: public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *pServerCharacteristic) {
    std::string rxValue = pServerCharacteristic->getValue();
    if (_bleCallback != NULL){
      _bleCallback(String(rxValue.c_str()));
    }
  }
};

class MyServerCallbacks: public BLEServerCallbacks {
    void onConnect(BLEServer* pServer) {
      deviceConnected = true;
    };

    void onDisconnect(BLEServer* pServer) {
      deviceConnected = false;
    }
};

void DFRobot_K10BleUart::bleServiceCallback(bleCallback callback)
{
  _bleCallback = callback;
}

void DFRobot_K10BleUart::setBleServer(char* name)
{
    BLEDevice::init(name);
    pServer = BLEDevice::createServer();
    pServer->setCallbacks(new MyServerCallbacks());
    BLEService *pService = pServer->createService(SERVICE_UUID);
    pServerCharacteristic = pService->createCharacteristic(
										CHARACTERISTIC_UUID,
										BLECharacteristic::PROPERTY_NOTIFY|BLECharacteristic::PROPERTY_WRITE
									);
    
                      
    pServerCharacteristic->setCallbacks(new MyCallbacks());

    // 开始服务器
    pService->start();

    // 开始广播
    BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
    pAdvertising->addServiceUUID(SERVICE_UUID);
    pAdvertising->setScanResponse(true);
    pAdvertising->setMinPreferred(0x06);  // functions that help with iPhone connections issue
    pAdvertising->setMinPreferred(0x12);
    BLEDevice::startAdvertising();

    xTaskCreatePinnedToCore(&taskBleServer, "taskBleServer", 2*1024, NULL, 2, NULL, ARDUINO_RUNNING_CORE);
    sendMode = 1;
}


void DFRobot_K10BleUart::setBleMaster(String name)
{
  _masterBleName = name;
  BLEDevice::init("");
  pBLEScan = BLEDevice::getScan(); 
  pBLEScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());
  pBLEScan->setInterval(100);    
  pBLEScan->setWindow(99);       
  pBLEScan->setActiveScan(true);
  pBLEScan->start(0, false);  
  xTaskCreatePinnedToCore(&taskBleMaster, "taskBleMaster", 4*1024, NULL, 2, NULL, ARDUINO_RUNNING_CORE);
  while(!deviceConnectedMaster){
    delay(100);
  }
}

bool DFRobot_K10BleUart::getConnectState(void)
{
  return deviceConnected;
}

void DFRobot_K10BleUart::sendData(String data){
    if(sendMode == 1){
      pServerCharacteristic->setValue(data.c_str());
      pServerCharacteristic->notify();
    }else{
      pMasterCharacteristic->writeValue(data.c_str(), data.length());
    }
}

void DFRobot_K10BleUart::sendData(float data){
  sendData(String(data, 2));
}


